package com.zhou.org.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zhou.org.common.OrgUtil;
import com.zhou.org.common.RightKeyEnum;
import com.zhou.org.dto.RightClearSelfDto;
import com.zhou.org.model.RightSaveBean;
import com.zhou.util.NumberUtil;
import com.zhou.util.ObjectUtil;
import com.zhou.util.StringTool;
import com.zhou.org.common.EnumConstant;
import com.zhou.org.common.RightUtil;
import com.zhou.org.dao.Right;
import com.zhou.org.dto.RightPageDto;
import com.zhou.org.dto.RightQueryDto;
import com.zhou.org.mapper.RightMapper;
import com.zhou.org.model.OrgTree;
import com.zhou.org.service.OrgService;
import com.zhou.org.service.RightService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author lang.zhou
 * @since 2022-08-23
 */
@Service
public class RightServiceImpl extends ServiceImpl<RightMapper, Right> implements RightService {
    @Autowired
    private OrgService orgService;
    @Override
    public List<String> getDistinctRightKey() {
        return baseMapper.getDistinctRightKey();
    }

    @Override
    public List<RightPageDto> getRight(RightQueryDto dto) {
        return baseMapper.getRight(dto);
    }

    private void deleteR(Right right, List<Integer> orgList){
        LambdaQueryWrapper<Right> w = new LambdaQueryWrapper<>();
        w.eq(Right::getRightKey,right.getRightKey());
        w.eq(Right::getRightType,right.getRightType());
        w.eq(Right::getObjectKey,right.getObjectKey());
        w.in(Right::getOrgId,orgList);
        remove(w);
    }

    /**
     * 将一个对象授权给多个组织结构
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveRights(Right right, List<Integer> orgList) {
        if(orgList.size() > 0){
            this.deleteR(right, orgList);
            for (Integer orgId : orgList) {
                Right r = new Right();
                BeanUtils.copyProperties(right,r);
                r.setOrgId(orgId);
                baseMapper.insert(r);
            }
        }
        if(RightKeyEnum.MENU.equals(right.getRightKey())){
            RightUtil.refreshMenuCache();
        }
    }

    @Override
    public void deleteRights(Collection<Integer> ids) {
        if(ObjectUtil.isNotEmpty(ids)){
            removeByIds(ids);
            RightUtil.refreshMenuCache();
        }
    }

    @Override
    public void deleteRight(String rightKey, String objectKey) {
        baseMapper.deleteRightByObjectId(rightKey,objectKey);
    }

    @Override
    public void deleteRight(String rightKey, String rightType, int orgId) {
        baseMapper.deleteRight(rightKey,rightType,orgId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteRight(String rightKey, Collection<String> objectKeys) {
        for (Object objectKey : objectKeys) {
            baseMapper.deleteRightByObjectId(rightKey, String.valueOf(objectKey));
        }
    }

    @Override
    public List<Right> getRightBy(String rightKey, String rightType, List<Integer> orgIds) {
        LambdaQueryWrapper<Right> query = new LambdaQueryWrapper<>();
        query.eq(Right::getRightKey,rightKey);
        query.eq(Right::getRightType,rightType);
        query.in(Right::getOrgId,orgIds);
        return list(query);
    }


    @Override
    public Map<String, Right> getRightMapBy(String rightKey, String rightType, int orgId) {
        List<Right> rights = this.getRightBy(rightKey, rightType, orgId);
        Map<String, Right> res = new HashMap<>(rights.size());
        for(Right right : rights){
            res.put(right.getObjectKey(),right);
        }
        return res;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void clearSelfRight(String rightKey, List<String> rightTypes, int orgId) {
        for (String rightType : rightTypes) {
            baseMapper.deleteRight(rightKey, rightType, orgId);
        }
        if(Objects.equals(rightKey, RightKeyEnum.MENU)){
            RightUtil.refreshMenuCache();
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void clearSelfRight(String rightKey, String rightType, int orgId) {
        baseMapper.deleteRight(rightKey, rightType, orgId);
        if(Objects.equals(rightKey, RightKeyEnum.MENU)){
            RightUtil.refreshMenuCache();
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void clearSelfRight(List<RightClearSelfDto> list) {
        if(list != null && list.size() > 0){
            for (RightClearSelfDto dto : list) {
                this.clearSelfRight(dto.getRightKey(),dto.getRightType(),dto.getOrgId());
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveRight(List<RightSaveBean> beans) {
        if(beans != null && beans.size() > 0){
            for (RightSaveBean bean : beans) {
                for(Right right : bean.getAdd()){
                    baseMapper.insert(right);
                }
                for(Right right : bean.getUpdate()){
                    baseMapper.updateById(right);
                }
                if(Objects.equals(bean.getRightKey(), RightKeyEnum.MENU)){
                    RightUtil.refreshMenuCache();
                }
            }
        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveRight(int orgId, String rightKey, Map<String, List<Right>[]> rights) {
        for (String rightType : rights.keySet()) {
            List<Right> add = rights.get(rightType)[0];
            List<Right> update = rights.get(rightType)[1];
            for(Right right : add){
                baseMapper.insert(right);
            }
            for(Right right : update){
                baseMapper.updateById(right);
            }
        }
        if(Objects.equals(rightKey, RightKeyEnum.MENU)){
            RightUtil.refreshMenuCache();
        }
    }

    @Override
    public List<Right> getRightBy(String rightKey, String rightType, int orgId) {
        List<OrgTree> orgList = new ArrayList<>(1);
        List<Integer> orgIdList = new ArrayList<>(1);
        OrgTree self = orgService.getOrgObject(orgId);

        if(self != null){
            List<OrgTree> parentCache = orgService.findLatestParentCache(orgId).stream()
                    .filter(o-> Objects.equals(o.getOrgType(),EnumConstant.ORG_TYPE_ROLE)).collect(Collectors.toList());
            self.setChilds(parentCache);
            orgIdList.addAll(parentCache.stream().map(OrgTree::getOrgId).collect(Collectors.toList()));
            orgIdList.add(orgId);
            orgList.add(self);
        }
        if(orgIdList.size() > 0){
            Map<Integer,Integer> orgType = new HashMap<>(orgIdList.size());
            Map<Integer,Integer> orgLevel = new HashMap<>(orgIdList.size());
            orgType.put(orgId,1);
            orgLevel.put(orgId,0);
            this.readOrg(orgList,orgType,orgLevel,1);
            List<List<Integer>> sss = new ArrayList<>();
            List<Integer> ss = new ArrayList<>();
            ss.add(orgId);
            if(orgList.size() == 0){
                sss.add(new ArrayList<>(ss));
            }else{
                this.readOrg2(orgList,sss,ss);
            }

            List<Right> rawList = this.getRightBy(rightKey,rightType,orgIdList);
            //合并上下级的权限
            //角色权限大于组织结构权限
            Map<String,Right> result = new HashMap<>();
            List<String> objectKeyList = new ArrayList<>();
            Map<String, Map<Integer,Right>> rightMap = new HashMap<>();
            for(Right right : rawList){
                String objectKey = StringTool.safeToString(right.getObjectKey(),"");
                int authOrgid = NumberUtil.safeToInteger(right.getOrgId(),0);

                if(!objectKeyList.contains(objectKey)){
                    objectKeyList.add(objectKey);
                }
                rightMap.computeIfAbsent(objectKey,o->new HashMap<>(1)).put(authOrgid,right);
            }
            for(String objectKey : objectKeyList){
                Map<Integer,Right> orgRightMap = rightMap.get(objectKey);
                if(orgRightMap != null && orgRightMap.size() > 0){
                    for(List<Integer> tree : sss){
                        for(Integer oid : tree){
                            Right right = orgRightMap.get(oid);
                            if(right != null ){
                                if(oid.equals(orgId)){
                                    result.put(objectKey,right);
                                }else if(!result.containsKey(objectKey)){
                                    result.put(objectKey,right);
                                }else{
                                    Right lastRight = result.get(objectKey);
                                    if(right.getReject() < lastRight.getReject()){
                                        result.put(objectKey,right);
                                    }
                                }
                                break;
                            }
                        }
                    }
                }

            }
            return new ArrayList<>(result.values());
        }
        return new ArrayList<>(0);
    }
    /**
     * 读取层级大小
     * @param orgType   orgid的组织结构类型
     * @param orgLevel  orgid的层级
     */
    private void readOrg(List<OrgTree> treeList,Map<Integer,Integer> orgType,Map<Integer,Integer> orgLevel,int t){
        for(OrgTree tree : treeList){
            orgType.put(tree.getOrgId(),tree.getOrgType());
            orgLevel.put(tree.getOrgId(),t);
            readOrg(tree.getChilds(),orgType,orgLevel,t++);
        }
    }
    /**
     * 读取层级大小
     */
    private void readOrg2(List<OrgTree> treeList, List<List<Integer>> sss, List<Integer> ss){
        for(OrgTree tree : treeList){
            ss.add(tree.getOrgId());
            if(tree.hasChilds()){
                readOrg2(tree.getChilds(),sss,ss);
            }else{
                sss.add(new ArrayList<>(ss));
            }
            ss.remove(tree.getOrgId());

        }
    }

}
